#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>

#include "globalPara.h"

/*
    IIC的阴间就不说了，数据线和时钟线；然后就是几种信号了
    1.起始信号、停止信号、应答信号、非应答信号、数据传输、
    IIC需要注意的是，起始信号和停止信号是主机发出的，数据传输是主机和从机都可以发出的
    读取和发送数据到从机，都是主机先发地址，然后从机应答，然后主机发送数据，然后从机应答
    
*/

/*
    IIC框架：  设备驱动 + 适配器 + 总线 + 从设备
    IIC 的 设备驱动 和 适配器驱动
    linux上的I2C框架，就是用户通过I2C驱动，调节适配器，来控制硬件

*/

/*
    重要结构和函数API：
    struct i2c_client: IIC从设备，包含从设备地址、从设备名称、从设备所属的IIC适配器等
    struct i2c_adapter: IIC适配器，包含IIC总线、IIC时钟、IIC数据线、IIC地址等
    struct i2c_device: IIC设备，包含IIC从设备、IIC适配器、IIC数据等
    i2c_register_driver：注册IIC从设备驱动
    i2c_del_driver：注销IIC从设备驱动
    i2c_add_driver：添加IIC从设备驱动
    i2c_transfer：IIC传输函数，用于发送和接收数据
    i2c_get_clientdata：获取IIC从设备私有数据
    i2c_set_clientdata：设置IIC从设备私有数据
    i2c_get_clientname：获取IIC从设备名称
    i2c_master_send：IIC主机发送数据
    i2c_master_recv：IIC主机接收数据
    适配器API：
    i2c_add_adapter：添加IIC适配器
    i2c_del_adapter：删除IIC适配器
    i2c_get_adapter：获取IIC适配器
    i2c_get_adaptername：获取IIC适配器名称
    i2c_get_adapter_id：获取IIC适配器ID
    i2c_get_adapter_nr：获取IIC适配器编号
    i2c_get_adapter_parent：获取IIC适配器父设备
    i2c_get_adapter_dev：获取IIC适配器设备
*/


/* MPU6050 驱动代码 */
/*
    流程： 
    1.初始化肯定是要的，所以需要一个初始化函数，进行设备的字符驱动初始化以及IIC设备初始化
    2.然后是设备close函数，当设备使用完后，就需要进行释放函数，进行设备的释放
    3.然后就是ioctl函数，这个函数是用户空间和内核空间通信的函数，用户可以通过这个函数，使用指定的某些命令，像传感器发送或则读取一些数据
    4.然后就是probe函数，这个函数是设备驱动注册函数，当设备插入后，就会调用这个函数，进行设备的初始化；同时将字符设备的操作函数绑定到IIC设备上，最后注册到内核中
    6.这时候设备的具体操作，通过字符设备的操作结构体 file_operators 进行绑定到 传感器对象的 cdev 上了；最后将传感器的IIC对象 i2c_client 注册到内核中
    7.然后就是设备注销函数，当设备拔出后，就会调用这个函数，进行设备的注销
    8.最后就是将 字符驱动和i2c_client设备驱动一起注册到 i2c_driver 上，通过 module_i2c_driver 挂到内核中

    一个IIC设备，一般是一个字符设备，同时加了一个协议，所以，这个设备驱动，就是字符设备驱动 + IIC协议驱动；
    需要将IIC驱动同字符设备进行绑定，通过一个大结构体中的 I2C_client来绑定传感器的协议，然后通过cdev来绑定字符设备驱动，最后将字符操作file_operatros 绑定到字符驱动中

i2c_driver  --> i2c_client
            --> cdev    -->  dev_t
                        -->  file_operators
*/

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
 
#include <linux/fs.h>
#include <linux/cdev.h>
 
#include <linux/slab.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
 
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/delay.h>
 
#include "mpu6050.h"
 
#define FSRTC_MAJOR	256
#define FSRTC_MINOR	9
#define FSRTC_DEV_NAME	"mpu6050"
 
/****************MPU6050内部常用寄存器地址****************/
#define SMPLRT_DIV	0x19
#define CONFIG		0x1A
#define GYRO_CONFIG	0x1B
#define ACCEL_CONFIG	0x1C
#define ACCEL_XOUT_H	0x3B
#define ACCEL_XOUT_L	0x3C
#define ACCEL_YOUT_H	0x3D
#define ACCEL_YOUT_L	0x3E
#define ACCEL_ZOUT_H	0x3F
#define ACCEL_ZOUT_L	0x40
#define TEMP_OUT_H	0x41
#define TEMP_OUT_L	0x42
#define GYRO_XOUT_H	0x43
#define GYRO_XOUT_L	0x44
#define GYRO_YOUT_H	0x45
#define GYRO_YOUT_L	0x46
#define GYRO_ZOUT_H	0x47
#define GYRO_ZOUT_L	0x48
#define PWR_MGMT_1	0x6B
 
struct mpu6050_dev {
	struct i2c_client *client;   // MPU6050从设备
	atomic_t available;   // 原子操作
	struct cdev cdev;    // 字符设备
};
 
static int mpu6050_open(struct inode *inode, struct file *filp)
{
	struct mpu6050_dev *mpu6050 = container_of(inode->i_cdev, struct mpu6050_dev, cdev);
 
	filp->private_data = mpu6050;
	if (atomic_dec_and_test(&mpu6050->available))
		return 0;
	else {
		atomic_inc(&mpu6050->available);
		return -EBUSY;
	}
}
 
static int mpu6050_release(struct inode *inode, struct file *filp)
{
	struct mpu6050_dev *mpu6050 = filp->private_data;
 
	atomic_inc(&mpu6050->available);
	return 0;
}
 
static long mpu6050_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct mpu6050_dev *mpu6050 = filp->private_data;
	struct atg_val val;
 
	if (_IOC_TYPE(cmd) != MPU6050_MAGIC)
		return -ENOTTY;
 
	switch (cmd) {
	case MPU6050_GET_VAL:
        // 读取16位数据 
		val.accelx = i2c_smbus_read_word_data(mpu6050->client, ACCEL_XOUT_H);
		val.accely = i2c_smbus_read_word_data(mpu6050->client, ACCEL_YOUT_H);
		val.accelz = i2c_smbus_read_word_data(mpu6050->client, ACCEL_ZOUT_H);
		val.temp   = i2c_smbus_read_word_data(mpu6050->client, TEMP_OUT_H);
		val.gyrox  = i2c_smbus_read_word_data(mpu6050->client, GYRO_XOUT_H);
		val.gyroy  = i2c_smbus_read_word_data(mpu6050->client, GYRO_YOUT_H);
		val.gyroz  = i2c_smbus_read_word_data(mpu6050->client, GYRO_ZOUT_H);
        /* 字节序转换 */
		val.accelx = be16_to_cpu(val.accelx);
		val.accely = be16_to_cpu(val.accely);
		val.accelz = be16_to_cpu(val.accelz);
		val.temp   = be16_to_cpu(val.temp);
		val.gyrox  = be16_to_cpu(val.gyrox);
		val.gyroy  = be16_to_cpu(val.gyroy);
		val.gyroz  = be16_to_cpu(val.gyroz);
		if (copy_to_user((struct atg_val __user *)arg, &val, sizeof(struct atg_val)))
			return -EFAULT;
		break;
	default:
		return -ENOTTY;
	}
 
	return 0;
}
 
static struct file_operations mpu6050_ops = {
	.owner = THIS_MODULE,
	.open = mpu6050_open,
	.release = mpu6050_release,
	.unlocked_ioctl = mpu6050_ioctl,
};
 
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	int ret;
	dev_t dev;
	struct mpu6050_dev *mpu6050;
    /*申请设备ID */
	dev = MKDEV(FSRTC_MAJOR, FSRTC_MINOR);
    /* 注册字符设备 */
	ret = register_chrdev_region(dev, 1, FSRTC_DEV_NAME);
	if (ret)
		goto reg_err;
    /*为MPU6050的设备申请内存*/
	mpu6050 = kzalloc(sizeof(struct mpu6050_dev), GFP_KERNEL);
	if (!mpu6050) {
		ret = -ENOMEM;
		goto mem_err;
	}
    /* 将自定义的结构体关联到 client 上  */
	i2c_set_clientdata(client, mpu6050);
	mpu6050->client = client;   // 关联 驱动对象 
    /* 绑定操作 */
	cdev_init(&mpu6050->cdev, &mpu6050_ops);
	mpu6050->cdev.owner = THIS_MODULE;
    // 将字符设备添加到系统中
	ret = cdev_add(&mpu6050->cdev, dev, 1);
	if (ret)
		goto add_err;
    // 检查I2C设备的适配器是否支持 SMBUS_WORD_DATA 功能
	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
		ret = -ENOSYS;
		goto fun_err;
	}
    // 初始化MPU6050
	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x80);
	msleep(200);
	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x40);
	i2c_smbus_write_byte_data(client, PWR_MGMT_1, 0x00);
 
	i2c_smbus_write_byte_data(client, SMPLRT_DIV,   0x7);
	i2c_smbus_write_byte_data(client, CONFIG,       0x6);
	i2c_smbus_write_byte_data(client, GYRO_CONFIG,  0x3 << 3); 
	i2c_smbus_write_byte_data(client, ACCEL_CONFIG, 0x3 << 3);
    // 初始化完成
	atomic_set(&mpu6050->available, 1);
 
	return 0;
 
fun_err:
	cdev_del(&mpu6050->cdev);
add_err:
	kfree(mpu6050);
mem_err:
	unregister_chrdev_region(dev, 1);
reg_err:
	return ret;
}
 
static int mpu6050_remove(struct i2c_client *client)
{
	dev_t dev;
	struct mpu6050_dev *mpu6050 = i2c_get_clientdata(client);
 
	dev = MKDEV(FSRTC_MAJOR, FSRTC_MINOR);
    /* 删除MPU6050设备 */
	cdev_del(&mpu6050->cdev);
	kfree(mpu6050);
    /* 注销字符设备 */
	unregister_chrdev_region(dev, 1);
	return 0;
}
 /* 用于匹配 I2C 设备驱动和硬件设备 */
static const struct i2c_device_id mpu6050_id[] = {
	{"mpu6050", 0},
	{}
};
 
/* 向内核中注册设备 ID 列表；便于内核可以将设备与相应的驱动程序匹配起来 */
MODULE_DEVICE_TABLE(i2c, mpu6050_id);
 
static struct i2c_driver mpu6050_driver = {
	.probe          =       mpu6050_probe,
	.remove         =       mpu6050_remove,
	.id_table       =       mpu6050_id,
	.driver = {
		.owner  =       THIS_MODULE,
		.name   =       "mpu6050",
	},
};
 /* 挂载 I2C 驱动*/
module_i2c_driver(mpu6050_driver);



MODULE_LICENSE("GPL");
MODULE_AUTHOR("name <e-mail>");
MODULE_DESCRIPTION("MPU6050 driver");







/*

int main(int argc, char *argv[])
{
	int fd;
	struct atg_val val;
 
 
	fd = open("/dev/mpu6050", O_RDWR);
 
	while (2) {
		ioctl(fd, MPU6050_GET_VAL, &val);
 
		printf("accelx: %.2f\n", val.accelx / 2048.0);
		printf("accely: %.2f\n", val.accely / 2048.0);
		printf("accelz: %.2f\n", val.accelz / 2048.0);
		printf("temp: %.2f\n", val.temp / 340.0 + 36.53); 
		printf("gyrox: %.2f\n", val.gyrox / 16.4);
		printf("gyroy: %.2f\n", val.gyroy / 16.4);
		printf("gyroz: %.2f\n", val.gyroz / 16.4);
 
 
		sleep(1);
	}
}


*/






